package com.yanqu.road.server.channelserver.manager.activity.luckydraw;

import com.yanqu.road.dao.impl.channelcenter.ChannelActivityDaoImpl;
import com.yanqu.road.dao.impl.channelcenter.luckydraw.LuckyDrawPoolDataDaoImpl;
import com.yanqu.road.dao.impl.channelcenter.luckydraw.LuckyDrawRecordDaoImpl;
import com.yanqu.road.dao.impl.channelcenter.luckydraw.LuckyDrawRewardConfigDaoImpl;
import com.yanqu.road.dao.impl.channelcenter.luckydraw.LuckyDrawUserDataDaoImpl;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawConfig;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawParam;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawPoolData;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawRecord;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawRewardConfig;
import com.yanqu.road.entity.activity.luckydraw.LuckyDrawUserData;
import com.yanqu.road.logic.config.ChannelConfig;
import com.yanqu.road.logic.pb.LuckyDrawPb2;
import com.yanqu.road.message.YanQuMessage;
import com.yanqu.road.pb.activity.LuckyDrawProto;
import com.yanqu.road.server.channelserver.logic.ChannelMessageHelper;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.Cross3Protocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.date.DateHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class ChannelLuckyDrawActivity {

    public static final Logger logger = LogManager.getLogger(ChannelLuckyDrawActivity.class.getName());

    private final Object lock = new Object(); // 大锁
    private final Random random = new Random();
    private LuckyDrawConfig config;
    private LuckyDrawPoolData luckyDrawPoolData;
    private Map<Long, LuckyDrawUserData> userDataMap;
    private List<LuckyDrawRecord> recordList;

    public ChannelLuckyDrawActivity(int activityId) {
        reloadConfig(activityId);
        reloadData();
    }

    public void reloadConfig(int activityId) {
        ActivityInfo activityInfo = new ChannelActivityDaoImpl().getActivityInfoById(activityId, ChannelConfig.CHANNEL_ACTIVITY_TIME_ZONE);
        if (activityInfo == null) {
            throw new RuntimeException("lucky draw config no found! activityId" + activityId);
        }

        LuckyDrawConfig tmpConfig = new LuckyDrawConfig();
        tmpConfig.setActivityInfo(activityInfo);
        tmpConfig.setRewardConfigList(new LuckyDrawRewardConfigDaoImpl().getLuckyDrawRewardConfigList(activityId));
        config = tmpConfig;
    }

    public void reloadData() {
        int activityId = config.getActivityInfo().getActivityId();

        userDataMap = new LuckyDrawUserDataDaoImpl().getLuckyDrawUserDataMap(activityId);

        luckyDrawPoolData = new LuckyDrawPoolDataDaoImpl().getLuckyDrawPoolData(activityId);
        if (luckyDrawPoolData == null) {
            luckyDrawPoolData = new LuckyDrawPoolData();
            luckyDrawPoolData.setActivityId(activityId);
            luckyDrawPoolData.setDrawTimesMap(new ConcurrentHashMap<>());
            luckyDrawPoolData.setTodayRealRewardTimes(0);
            luckyDrawPoolData.setResetTime(System.currentTimeMillis());
            luckyDrawPoolData.setInsertOption();
        }

        recordList = new LuckyDrawRecordDaoImpl().getLuckyDrawRecord(activityId);
    }

    private LuckyDrawUserData getUserData(LuckyDrawParam param) {
        synchronized (lock) {
            // 获取玩家数据
            long userId = param.getUserId();
            if (!userDataMap.containsKey(userId)) {
                LuckyDrawUserData userData = new LuckyDrawUserData();
                userData.setActivityId(config.getActivityInfo().getActivityId());
                userData.setUserId(userId);
                userData.setResetTime(param.getZeroTime());
                userData.setCanDrawTimes(0);
                userData.setDayDrawTimes(0);
                userData.setWasheDayWinTimes(0);
                userData.setZeroTime(param.getZeroTime());
                userData.setInsertOption();
                userDataMap.put(userId, userData);
            }
            LuckyDrawUserData userData = userDataMap.get(userId);

            // 判断重置
            long now = System.currentTimeMillis();
            long zeroTime = DateHelper.getZeroTimeStamp(now); // zkt wait todo 未做时区判断
            if (userData.getResetTime() < zeroTime) {
                userData.setResetTime(now);
                userData.setCanDrawTimes(0);
                userData.setDayDrawTimes(0);
                userData.setWasheDayWinTimes(0);
            }

            return userData;
        }
    }

    private LuckyDrawPoolData getPoolData() {
        synchronized (lock) {
            long now = System.currentTimeMillis();
            long zeroTime = DateHelper.getZeroTimeStamp(now);
            if (luckyDrawPoolData.getResetTime() < zeroTime) {
                luckyDrawPoolData.setResetTime(now);
                luckyDrawPoolData.setTodayRealRewardTimes(0);
            }
        }
        return luckyDrawPoolData;
    }

    public void save() {

        // 池子
        try {
            if (luckyDrawPoolData.isInsertOption()) {
                new LuckyDrawPoolDataDaoImpl().add(luckyDrawPoolData);
            } else if (luckyDrawPoolData.isUpdateOption()) {
                new LuckyDrawPoolDataDaoImpl().update(luckyDrawPoolData);
            }
        } catch (Exception e) {
            logger.error("error : ", e);
        }

        // 玩家数据
        for (LuckyDrawUserData userData : userDataMap.values()) {
            try {
                if (userData.isInsertOption()) {
                    new LuckyDrawUserDataDaoImpl().add(userData);
                } else if (userData.isUpdateOption()) {
                    new LuckyDrawUserDataDaoImpl().update(userData);
                }
            } catch (Exception e) {
                logger.error("error : ", e);
            }
        }

        // 公告
        for (LuckyDrawRecord record : recordList) {
            try {
                if (record.isInsertOption()) {
                    new LuckyDrawRecordDaoImpl().add(record);
                }
            } catch (Exception e) {
                logger.error("error : ", e);
            }
        }

    }

    public LuckyDrawConfig getConfig() {
        return config;
    }

    // --------------------------------------

    /**
     * 随机一份奖励
     */
    private LuckyDrawRewardConfig randomDrawReward(LuckyDrawParam param) {
        List<LuckyDrawRewardConfig> configList = config.getRewardConfigList();

        synchronized (lock) {

            LuckyDrawPoolData poolData = getPoolData();
            Map<Integer, Integer> drawTimesMap = poolData.getDrawTimesMap();
            boolean realRewardDayLimit = poolData.getTodayRealRewardTimes() >= Integer.valueOf(config.getActivityInfo().getParam4());

            List<LuckyDrawRewardConfig> realList = new ArrayList<>();
            List<LuckyDrawRewardConfig> normalList = new ArrayList<>();
            for (LuckyDrawRewardConfig rewardConfig : configList) {
                // 实物奖励：去掉没有数量的奖励
                if (!realRewardDayLimit && rewardConfig.getNum() >= 0 && drawTimesMap.getOrDefault(rewardConfig.getShootId(), 0) < rewardConfig.getNum()) {
                    realList.add(rewardConfig);
                } else {
                    // 非实物奖励
                    if (rewardConfig.getNum() == -1) {
                        normalList.add(rewardConfig);
                    }
                }
            }

            // 抽池子
            List<Integer> poolWeightList = StringUtils.stringToIntegerList(config.getActivityInfo().getParam5(), "\\|"); // 普通概率|实物概率
            int poolIndex = RandomHelper.getRandomIndexByWeight(poolWeightList, random);

            boolean drawReal = poolIndex == 1;
            List<LuckyDrawRewardConfig> canDrawList;
            if (drawReal && realList.size() > 0) {
                canDrawList = realList;
            } else {
                canDrawList = normalList;
            }
            List<Integer> canDrawWeightList = canDrawList.stream().mapToInt(LuckyDrawRewardConfig::getWeight).boxed().collect(Collectors.toList());

            // 抽奖励
            int index = RandomHelper.getRandomIndexByWeight(canDrawWeightList, random);
            LuckyDrawRewardConfig rewardConfig = canDrawList.get(index);

            // 记录
            if (rewardConfig.getNum() >= 0) {
                int times = drawTimesMap.getOrDefault(rewardConfig.getShootId(), 0);
                drawTimesMap.put(rewardConfig.getShootId(), times + 1);
                poolData.setTodayRealRewardTimes(poolData.getTodayRealRewardTimes() + 1);
                poolData.setUpdateOption();

                // 公告
                LuckyDrawRecord record = new LuckyDrawRecord(
                        config.getActivityInfo().getActivityId(),
                        param.getChannelId(),
                        param.getServerId(),
                        param.getUserId(),
                        param.getNickname(),
                        rewardConfig.getReward(),
                        System.currentTimeMillis() // zkt wait todo 时区暂时不管
                );
                record.setInsertOption();
                recordList.add(record);
                // 同步公告给所有人
                LuckyDrawProto.LuckyDrawSyncRecordRespMsg.Builder builder = LuckyDrawProto.LuckyDrawSyncRecordRespMsg.newBuilder();
                builder.addRecord(LuckyDrawPb2.parseLuckyDrawRecordMsg(record));
                LuckyDrawProto.ChannelLuckyDrawSyncRecordRespMsg.Builder transfer = LuckyDrawProto.ChannelLuckyDrawSyncRecordRespMsg.newBuilder();
                transfer.setMsg(builder);
                transfer.addAllServerId(config.getActivityInfo().getServerIdList());
                YanQuMessage message = YanQuMessageUtils.buildMessage(Cross3Protocol.C3_LUCKY_DRAW_TRANSFER_RECORD, transfer);
                ChannelMessageHelper.sendAllChannel(message);
            }

            return rewardConfig;
        }
    }

    /**
     * 抽
     */
    private int draw(LuckyDrawParam param) {
        LuckyDrawUserData userData = getUserData(param);
        synchronized (lock) {
            if (userData.getCanDrawTimes() <= 0) {
                return GameErrorCode.E_LUCKY_DRAW_NO_TIMES;
            }

            // 扣次数
            userData.setCanDrawTimes(userData.getCanDrawTimes() - 1);
            userData.setDayDrawTimes(userData.getDayDrawTimes() + 1);

            // 抽
            LuckyDrawRewardConfig rewardConfig = randomDrawReward(param);

            // 返回奖励
            LuckyDrawProto.LuckyDrawRespMsg.Builder builder = LuckyDrawProto.LuckyDrawRespMsg.newBuilder();
            builder.setRet(0);
            builder.setReward(rewardConfig.getReward());
            builder.setId(rewardConfig.getShootId());
            ChannelMessageHelper.sendGamePacket(GameProtocol.S_LUCKY_DRAW_DRAW_FROM_CHANNEL, builder, param.getChannelId(), param.getServerId(), param.getUserId());
            return 0;
        }
    }

    // --------------------------------------

    /**
     * 获取抽奖公告
     */
    public void getRecordListByCmd(LuckyDrawParam param) {
        ArrayList<LuckyDrawRecord> list = new ArrayList<>(recordList);
        int startIndex = Math.max(0, list.size() - 30); // 最多下发20条

        LuckyDrawProto.LuckyDrawRecordRespMsg.Builder builder = LuckyDrawProto.LuckyDrawRecordRespMsg.newBuilder();
        builder.setRet(0);
        for (int i = startIndex; i < list.size(); i++) {
            builder.addRecord(LuckyDrawPb2.parseLuckyDrawRecordMsg(list.get(i)));
        }

        ChannelMessageHelper.sendGamePacket(ClientProtocol.U_LUCKY_DRAW_GET_RECORD, builder, param.getChannelId(), param.getServerId(), param.getUserId());
    }

    /**
     * 抽奖
     */
    public void drawByCmd(LuckyDrawParam param) {
        int ret = draw(param);
        if (ret != 0) {
            LuckyDrawProto.LuckyDrawRespMsg.Builder builder = LuckyDrawProto.LuckyDrawRespMsg.newBuilder();
            builder.setRet(ret);
            ChannelMessageHelper.sendGamePacket(ClientProtocol.U_LUCKY_DRAW_DRAW, builder, param.getChannelId(), param.getServerId(), param.getUserId());
        }
    }

    /**
     * 增加瓦舍获胜次数
     */
    public void addWasheWinTimesByCmd(LuckyDrawParam param) {
        LuckyDrawUserData userData = getUserData(param);
        synchronized (lock) {
            // 加获胜次数
            int times = userData.getWasheDayWinTimes();
            userData.setWasheDayWinTimes(times + 1);
            // 首胜：加抽奖次数
            if (times == 0) {
                userData.setCanDrawTimes(userData.getCanDrawTimes() + 1);
            }
        }
    }

    /**
     * 获取玩家数据
     */
    public void getUserDataByCmd(LuckyDrawParam param) {
        LuckyDrawUserData userData = getUserData(param);
        LuckyDrawProto.LuckyDrawGetUserDataRespMsg.Builder builder = LuckyDrawProto.LuckyDrawGetUserDataRespMsg.newBuilder();
        builder.setDailyDrawCount(userData.getDayDrawTimes());
        builder.setDailyFirstWinCount(userData.getWasheDayWinTimes());
        ChannelMessageHelper.sendGamePacket(ClientProtocol.U_LUCKY_DRAW_GET_USER_DATA, builder, param.getChannelId(), param.getServerId(), param.getUserId());
    }

}
